﻿//Copyright 2011 - Jonathan Allen

using System;
using System.Windows.Input;
using System.Diagnostics.Contracts;
using System.Diagnostics.CodeAnalysis;

namespace Granite.Xaml
{
	/// <summary>
	/// Creates a simple ICommand based on a delegate or pair of delegates.
	/// </summary>
	public class DelegateCommand : ICommand
	{
		readonly Func<object, bool> m_CanExecute;

		readonly Action<object> m_Command;

		private event EventHandler m_CanExecuteChanged;

		/// <summary>
		/// Occurs when changes occur that affect whether or not the command should execute.
		/// </summary>
		/// <remarks> This event only exists if a CanExecute delegate is provided. Otherwise add/remove are no-ops.</remarks>
		public event EventHandler CanExecuteChanged
		{
			add
			{
				if (m_CanExecute != null)
					m_CanExecuteChanged += value;
			}
			remove
			{
				if (m_CanExecute != null)
					m_CanExecuteChanged -= value;
			}
		}

		/// <summary>
		/// Creates a Delegate command
		/// </summary>
		/// <param name="command"></param>
		/// <param name="canExecute"></param>
		public DelegateCommand(Action command, Func<bool> canExecute = null)
		{
			if (command == null)
				throw new ArgumentNullException("command", "command is null.");
			Contract.EndContractBlock();

			m_Command = (param) => command();
			if (canExecute != null)
				m_CanExecute = (param) => canExecute();

		}

		/// <summary>
		/// Creates a Delegate command
		/// </summary>
		/// <param name="command"></param>
		/// <param name="canExecute"></param>
		public DelegateCommand(Action<object> command, Func<object, bool> canExecute = null)
		{
			if (command == null)
				throw new ArgumentNullException("command", "command is null.");
			Contract.EndContractBlock();

			m_Command = command;
			if (canExecute != null)
				m_CanExecute = canExecute;
		}

		/// <summary>
		/// Defines the method that determines whether the command can execute in its current state.
		/// </summary>
		/// <returns>
		/// true if this command can be executed; otherwise, false.
		/// </returns>
		/// <param name="parameter">
		/// Data used by the command.  If the command does not require data to be passed, this object can be set to null.
		/// </param>
		public bool CanExecute(object parameter)
		{
			if (m_CanExecute == null)
				return true;
			else
				return m_CanExecute(parameter);
		}

		/// <summary>
		/// Defines the method to be called when the command is invoked.
		/// </summary>
		/// <param name="parameter">
		/// Data used by the command.  If the command does not require data to be passed, this object can be set to null.
		/// </param>
		public void Execute(object parameter)
		{
			m_Command(parameter);
		}

		/// <summary>
		/// Raises a CanExecuteChanged event.
		/// </summary>
		public void OnCanExecuteChanged()
		{
			if (m_CanExecuteChanged != null)
				m_CanExecuteChanged(this, EventArgs.Empty);
		}

		[SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic"), SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
		[ContractInvariantMethod]
		void ObjectInvariant()
		{
			Contract.Invariant(m_Command != null);
		}
		
	}
}
